classdef Simulator
    properties
    end

    methods(Static)
        function [cost, path] = find_and_draw_dijkstra_path(topology, source_node_id, dest_node_id)
            TopologyDrawer.draw(topology); % clears the last shortest path from the graph
            [shortest_paths_costs, pred_vector] = dijkstra(topology.costs_matrix, source_node_id);
            cost = shortest_paths_costs(dest_node_id);
            path = get_path_from_pred_vector(pred_vector, dest_node_id);
            TopologyDrawer.draw_path(topology, path, '-.', 'r', 2.5);
            hold off;
        end
        
        function statistics = run_single_depositor_in_unloaded_network(topology, source_node_id, dest_node_id, packet_size_in_bytes, number_of_packets_to_deposit, ticks_to_deposit_packets)
            params = ParamsSingleton.instance();
            
            SingleNodePacketDepositor(topology.nodes(source_node_id),topology.nodes(dest_node_id),packet_size_in_bytes,number_of_packets_to_deposit,ticks_to_deposit_packets);
            
            Simulator.start_running(topology, false);
            
            statistics_manager = StatisticsManager.instance();
            statistics = statistics_manager.get_and_print_statistics();
        end
        
        function find_and_draw_romer_paths(topology, source_node_id, dest_node_id, should_draw_routes)
            params = ParamsSingleton.instance();
            params.number_of_packets_to_deposit_in_single_node_simulation = 1;
            
            ticks_to_deposit_packets=(1);
            packet_size_in_bytes=500;
            number_of_packets_to_deposit = 1;
            SingleNodePacketDepositor(topology.nodes(source_node_id),topology.nodes(dest_node_id),packet_size_in_bytes,number_of_packets_to_deposit,ticks_to_deposit_packets);
            
           Simulator.start_running(topology, should_draw_routes);
        end

        function statistics = simulate_multiple_connections(topology)
            % simulate romer:
            % save('temp_topology.mat', 'topology', '-mat');
            traffic_manager = TrafficManager(topology);
            traffic_manager.simulate_multiple_connections();
            Simulator.start_running(topology, false);
            
            statistics_manager = StatisticsManager.instance();
            statistics = statistics_manager.get_and_print_statistics();
            
            % simulate dijkstra:
            % param = load('temp_topology.mat', '-mat', 'topology');
            % topology = param.topology;
            % topology.set_dijkstra_router();
            % traffic_manager = TrafficManager(topology);
            % traffic_manager.simulate_multiple_connections();
            % Simulator.start_running(topology, false);
        end
        
        function simulate_file_download_packet_generator(topology, source_node_id, dest_node_id)
            params = ParamsSingleton.instance();
            
            FileDownloadPacketsGenerator(topology.nodes(source_node_id),...
                                         topology.nodes(dest_node_id),...
                                         params.file_download_packet_size_in_bytes,...
                                         params.file_download_data_rate_in_Kbps);
            
            Simulator.start_running(topology, true);
        end
        
        function simulate_web_browsing_packet_generator(topology, source_node_id, dest_node_id)
            params = ParamsSingleton.instance();
            
            WebBrowsingPacketsGenerator(topology.nodes(source_node_id),...
                                        topology.nodes(dest_node_id),...
                                        params.web_browsing_packet_size_in_bytes,...
                                        params.web_browsing_data_rate_in_Kbps);
            
            Simulator.start_running(topology, true);
        end
        
        function simulate_voip_packet_generator(topology, source_node_id, dest_node_id)
            params = ParamsSingleton.instance();
            
            VoipPacketsGenerator(topology.nodes(source_node_id),...
                                 topology.nodes(dest_node_id),...
                                 params.voip_packet_size_in_bytes,...
                                 params.voip_data_rate_in_Kbps);
            
            Simulator.start_running(topology, true);
        end
        
        function run_clock(topology)
            params = ParamsSingleton.instance();
            tick_counts = params.simulation_time_in_msec / params.clock_tick_delta_in_msec;
            
            ck = ClockSingleton.instance();
            for i = 1:tick_counts
                ck.tick();
            end
            
            params.should_disable_all_traffic_generators = true;
            
            network_dead = false;
            while ~network_dead
                ck.tick();
                network_dead =  true;
                for j=1:length(topology.nodes)
                    if ~topology.nodes(j).is_node_idle()
                        network_dead = false;
                        break;
                    end
                end
            end
        end
        
        function print_delivered_packets_routes(topology, should_draw_routes)
            Utils.instance().trace(sprintf('\r\n\r\n-------------------------------\r\n'));
            Utils.instance().trace(sprintf('Packets delivery summary:\r\n'));
            packets_manager = PacketsManager.instance();
            
            total_routes_matrix = zeros(length(packets_manager.delivered_multi_packets), topology.number_of_hap_nodes + 1);
            
            for i=1:length(packets_manager.delivered_multi_packets)
                currnet_packet = packets_manager.delivered_multi_packets(i).packet;
                Utils.instance().trace(sprintf('packet id: %d. source node: %d.  dest node: %d. route length: %d\r\n', currnet_packet.id, currnet_packet.src_node.id, currnet_packet.dest_node.id, length(currnet_packet.route)));
                packet_route = currnet_packet.route;
                total_routes_matrix(i, 1:length(packet_route)) = packet_route;                
                
                Simulator.print_route(packet_route);

                if (should_draw_routes)
                    TopologyDrawer.draw_path(topology, packet_route, '--', 'm', 2.0);
                end
            end
            
            different_paths = unique(total_routes_matrix, 'rows');
            number_of_different_paths = size(different_paths, 1);
            Utils.instance().trace(sprintf('\r\nNumber of different routes found: %d\r\n', number_of_different_paths));
        end
        
        function print_route(packet_route)
            Utils.instance().trace(sprintf('Packet route: '));
            for j = 1:length(packet_route);
                if j ~= length(packet_route)
                    Utils.instance().trace(sprintf('%d =>',packet_route(j)));
                else
                    Utils.instance().trace(sprintf('%d\r\n\r\n',packet_route(j)));
                end
            end 
        end
        
        function start_running(topology, should_draw_routes)
            % SimulationProgressBarSingleton.instance();
            Simulator.run_clock(topology);
            Simulator.print_delivered_packets_routes(topology, should_draw_routes);
        end
        
    end
end